package com.atguigu.gmall.item.service.impl;

import com.alibaba.nacos.common.utils.ConcurrentHashSet;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.common.util.Jsons;
import com.atguigu.gmall.item.feign.SkuDetailFeignClient;
import com.atguigu.gmall.item.service.SkuDetailService;
import com.atguigu.gmall.product.entity.SkuImage;
import com.atguigu.gmall.product.entity.SkuInfo;
import com.atguigu.gmall.product.entity.SpuSaleAttr;
import com.atguigu.gmall.web.SkuDetailVo;
import com.google.common.collect.Lists;
import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import java.math.BigDecimal;
import java.util.List;
import java.util.Set;

@Slf4j
@Service
public class SkuDetailServiceImpl implements SkuDetailService {

    @Autowired
    SkuDetailFeignClient skuDetailFeignClient;

    //byte
    BloomFilter<Long> bloomFilter = null;

    @Autowired
    StringRedisTemplate redisTemplate;

    @PostConstruct//对象构造器初始化创建对象之后执行这个方法
    void init() {
        //这个组件创建好以后会执行这个方法
        log.info("项目启动：正在初始化布隆过滤器");
        bloomFilter = BloomFilter.create(Funnels.longFunnel(), 5000000, 0.0000001);

        //获取到所有skuId；
        Result<List<Long>> skuId = skuDetailFeignClient.getAllSkuId();
        skuId.getData().forEach(item -> {
            bloomFilter.put(item);
        });
        log.info("布隆过滤器初始化完成...49：{} 77：{}", bloomFilter.mightContain(49L), bloomFilter.mightContain(77L));

    }

    //加入缓存 5000000 600w+
    //1、Object data = cache.get(skuId) //88 - x 600xxx - x
    // if (data == null) //回源
    //if（skuId.contains(skuId)） //数据库中有次记录 //回源 else return null;
    //有：return data;
    @Override
    public SkuDetailVo skuDetail(Long skuId) {
        log.info("商品详情查询开始：{}", skuId);
        if (!bloomFilter.mightContain(skuId)) {
            //1、布隆说没有
            log.info("商品详情-布隆过滤器拦截-数据库中没有直接返回null：{}", skuId);
            return null;
        }

        //2、先查缓存
        log.info("商品详情-正在数据回源：{}", skuId);
        String json = redisTemplate.opsForValue().get("sku:info:" + skuId);
        if (!StringUtils.isEmpty(json)) {
            //3、redis中有缓存数据; x或真实值
            SkuDetailVo detailVo = Jsons.toObj(json, SkuDetailVo.class);
            return detailVo;
        }

        //4、redis中没有
        SkuDetailVo fromRpc = getSkuDetailFromRpc(skuId);
        //把对象转为json字符串
        String data = fromRpc == null ? "x" : Jsons.toStr(fromRpc);

        //保存到redis
        redisTemplate.opsForValue().set("sku:info:" + skuId, data);
        log.info("商品详情-数据回源完成-并以缓存到redis：{}", fromRpc);
        return fromRpc;
    }

    private SkuDetailVo getSkuDetailFromRpc(Long skuId) {
        SkuDetailVo data = new SkuDetailVo();
        //1、查询出sku_info信息
        Result<SkuInfo> skuInfo = skuDetailFeignClient.getSkuInfo(skuId);

        //2。查询当前sku的图片列表
        Result<List<SkuImage>> skuImages = skuDetailFeignClient.getSkuImages(skuId);

        SkuInfo info = skuInfo.getData();
        info.setSkuImageList(skuImages.getData());

        //设置好skuinfo的数据返回
        data.setSkuInfo(info);

        Result<SkuDetailVo.CategoryView> categoryView = skuDetailFeignClient.getCategoryView(info.getCategory3Id());

        //3、查询sku的分类信息
        data.setCategoryView(categoryView.getData());

        //4、查询sku的价格信息。为了得到最新价格，每次都再查一遍
        Result<BigDecimal> skuPrice = skuDetailFeignClient.getSkuPrice(skuId);
        data.setPrice(skuPrice.getData());

        //5、查询sku的销售属性
        Long spuId = info.getSpuId();
        Result<List<SpuSaleAttr>> value = skuDetailFeignClient.getSpuSaleAttrAndValue(spuId, skuId);
        data.setSpuSaleAttrList(value.getData());

        //6、json 就是 map、 （“119|120”：49， “122：123：50”）、javaBean
        Result<String> skuJson = skuDetailFeignClient.getSpuValuesSkuJson(spuId);
        data.setValuesSkuJson(skuJson.getData());

        return data;
    }
}
